import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * TreeMap implementation of <code>MarkovIO</code>
 */
public class MarkovIOTree extends MarkovIO
{
	private Map<MarkovStateSet, MarkovPTable> data;
	private String dataFile;
	private FileOutputStream fostream;
	private ObjectOutputStream oostream;
	private FileInputStream fistream; //new FileInputStream("tree.tmp");
    private ObjectInputStream oistream;//new ObjectInputStream(istream);
	
    /**
	 * Creates/opens a MarkovIOTree storage file (named String file), and 
	 * loads TreeMap<MarkovStates, MarkovPTable> data from it (if any)
	 * 
	 * @param file <code>String</code> Name of IO file for this MarkovIOTree
	 */
	public MarkovIOTree(String file)
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovIOTree.MarkovIOTree ("+file+")");
		
		data = new HashMap<MarkovStateSet, MarkovPTable>();
		
		try
		{
			if(file != null)
			{
				dataFile = new String(file);
				this.loadData();
			} 
			else 
			{
				dataFile = null;
			}
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
		}
		
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovIOTree.MarkovIOTree");
	}
	
	/**
	 * Loads the MarkovState map from dataFile
	 */
	@SuppressWarnings("unchecked")
	private void loadData()
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovIOTree.loadData ()");
		
		MarkovStateMgr stateMgr = MarkovStateMgr.getSingleton();
		
		if(this.dataFile != null){ 
			try{
				//TODO: Check if file has data before possibly overwriting it.
				fistream = new FileInputStream(dataFile);
				oistream = new ObjectInputStream(fistream);
				Map<MarkovStateSet, MarkovPTable> priorData = 
					new HashMap<MarkovStateSet, MarkovPTable>(
						(HashMap<MarkovStateSet, MarkovPTable>)oistream.readObject());
				
				for(MarkovStateSet priorStateSet : priorData.keySet())
				{
					MarkovStateSet curState = 
						stateMgr.getMarkovStateSet(priorStateSet);
					
					// Recreate PTable with current MarkovStates.
					MarkovPTable tempTable = priorData.get(priorStateSet);
					for(MarkovState iState : tempTable.getMarkovStates())
					{
						tempTable.updateMarkovState(iState, 
								stateMgr.getMarkovState(iState.getToken()));
					}
					data.put(curState, tempTable);
				}
				
				fistream.close();
				oistream.close();
			} catch (Exception ex){
				ex.printStackTrace();
			}	
		}
		
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovIOTree.loadData");
	}
	
	/**
	 * Saves the current MarkovState map to dataFile 
	 * This is automatically called every time setPTable is called
	 */
	private void saveData()
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovIOTree.saveData ()");
		
		if(this.dataFile != null){ 
			try{
				fostream = new FileOutputStream(dataFile, false);	
				oostream = new ObjectOutputStream(fostream);
				oostream.writeObject(this.data);
				oostream.flush();
				oostream.close();
				fostream.close();
			} catch (Exception ex){
				ex.printStackTrace();
			}	
		}
		
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovIOTree.saveData");
	}	
	
	@Override
	/**
	 * Returns the MarkovPTable for given MarkovStateSet
	 * 
	 * @param state <code>MarkovStateSet</code> state to retrieve from the TreeMap
	 * @return <code>MarkovPTable</code> that linked to MarkovStates
	 */
	public MarkovPTable getPTable(MarkovStateSet states) 
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovIOTree.getPTable ("+states+")");
		
		MarkovPTable tableRequest = data.get(states);
		
		if(tableRequest == null)
		{
			tableRequest = new MarkovPTable(states);
			setPTable(states, tableRequest);
		}
		

		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovIOTree.getPTable ("+tableRequest+")");
		return tableRequest;		
	}	
	
	@Override
	protected MarkovPTable setPTable(MarkovStateSet states, MarkovPTable ptable)
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovIOTree.setPTable ("+states+","+ptable+")");
		
		// Store the given PTablein the TreeMap and write it to the storage 
		// file
		MarkovPTable retobj = this.data.put(states,ptable);
//		if(retobj != null)
//			System.err.println("WARNING: MarkovPTable.setPTable COLLISION??!!??!?!??!");
		
		this.saveData();
		
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovIOTree.setPTable ("+retobj+")");
		return ptable;
	}
}
